/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuras.h"
#include "blood_furnace.h"

enum eKelidan
{
    SAY_WAKE                    = 0,
    SAY_ADD_AGGRO               = 1,
    SAY_KILL                    = 2,
    SAY_NOVA                    = 3,
    SAY_DIE                     = 4,

	// Keldian spells
    SPELL_CORRUPTION            = 30938,
    SPELL_EVOCATION             = 30935,
    SPELL_FIRE_NOVA             = 33132,
    SPELL_SHADOW_BOLT_VOLLEY    = 28599,
    SPELL_BURNING_NOVA          = 30940,
    SPELL_VORTEX                = 37370,

	// Channelers spells
	SPELL_SHADOW_BOLT				= 12739,
    SPELL_SHADOW_BOLT_H				= 15472,
    SPELL_MARK_OF_SHADOW			= 30937,
    SPELL_CHANNELING				= 39123,

	// Events
	EVENT_SPELL_VOLLEY				= 1,
	EVENT_SPELL_CORRUPTION			= 2,
	EVENT_SPELL_BURNING_NOVA		= 3,
	EVENT_SPELL_FIRE_NOVA			= 4,
	EVENT_SPELL_SHADOW_BOLT			= 5,
	EVENT_SPELL_MARK				= 6,

	// Actions
	ACTION_CHANNELER_ENGAGED		= 1,
	ACTION_CHANNELER_DIED			= 2,
};

const float ShadowmoonChannelers[5][4] =
{
    {302.0f, -87.0f, -24.4f, 0.157f},
    {321.0f, -63.5f, -24.6f, 4.887f},
    {346.0f, -74.5f, -24.6f, 3.595f},
    {344.0f, -103.5f, -24.5f, 2.356f},
    {316.0f, -109.0f, -24.6f, 1.257f}
};

class boss_kelidan_the_breaker : public CreatureScript
{
    public:

        boss_kelidan_the_breaker() : CreatureScript("boss_kelidan_the_breaker")
        {
        }

        struct boss_kelidan_the_breakerAI : public ScriptedAI
        {
            boss_kelidan_the_breakerAI(Creature* creature) : ScriptedAI(creature)
            {
                instance = creature->GetInstanceScript();
				memset(&channelers, 0, sizeof(channelers));
            }

            InstanceScript* instance;
			EventMap events;
			uint64 channelers[5];
			uint32 checkTimer;
            bool addYell;
            
            void Reset()
            {
                addYell = false;
				checkTimer = 5000;

				events.Reset();
				ApplyImmunities(true);
                SummonChannelers();
                me->SetReactState(REACT_PASSIVE);
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE);
                if (instance)
                    instance->SetData(DATA_KELIDAN, NOT_STARTED);
            }

            void EnterCombat(Unit* who)
            {
				events.ScheduleEvent(EVENT_SPELL_VOLLEY, 1000);
				events.ScheduleEvent(EVENT_SPELL_CORRUPTION, 5000);
				events.ScheduleEvent(EVENT_SPELL_BURNING_NOVA, 15000);

				me->InterruptNonMeleeSpells(false);
                Talk(SAY_WAKE);

				if (instance)
					instance->SetData(DATA_KELIDAN, IN_PROGRESS);
            }

            void KilledUnit(Unit* /*victim*/)
            {
                if (urand(0,1))
					Talk(SAY_KILL);
            }

			void DoAction(int32 param)
			{
				if (param == ACTION_CHANNELER_ENGAGED)
				{
					if (!addYell)
					{
						addYell = true;
						Talk(SAY_ADD_AGGRO);
					
						for (uint8 i = 0; i < 5; ++i)
						{
							Creature* channeler = ObjectAccessor::GetCreature(*me, channelers[i]);
							if (channeler && !channeler->IsInCombat())
								channeler->SetInCombatWithZone();
						}
					}
				}
				else if (param == ACTION_CHANNELER_DIED)
				{
					for (uint8 i = 0; i < 5; ++i)
					{
						Creature* channeler = ObjectAccessor::GetCreature(*me, channelers[i]);
						if (channeler && channeler->IsAlive())
							return;
					}
					me->SetReactState(REACT_AGGRESSIVE);
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE);
					if (Unit* target = me->SelectNearestPlayer(100.0f))
						AttackStart(target);
				}
			}

            void CheckChannelers()
            {
				if (addYell)
				{
					if (!SelectTargetFromPlayerList(100.0f))
						EnterEvadeMode();
					return;
				}

				SummonChannelers();
                for (uint8 i = 0; i < 5; ++i)
                {
                    Creature* channeler = ObjectAccessor::GetCreature(*me, channelers[i]);
                    if (channeler && !channeler->HasUnitState(UNIT_STATE_CASTING) && !channeler->IsInCombat())
					{
						Creature* target = ObjectAccessor::GetCreature(*me, channelers[(i+2)%5]);
						if (target)
							channeler->CastSpell(target, SPELL_CHANNELING, false);
					}
                }
            }

            void SummonChannelers()
            {
                for (uint8 i = 0; i < 5; ++i)
                {
                    Creature* channeler = ObjectAccessor::GetCreature(*me, channelers[i]);
					if (channeler && channeler->isDead())
					{
						channeler->DespawnOrUnsummon(1);
						channeler = NULL;
					}
                    if (!channeler)
                        channeler = me->SummonCreature(NPC_CHANNELER, ShadowmoonChannelers[i][0], ShadowmoonChannelers[i][1], ShadowmoonChannelers[i][2], ShadowmoonChannelers[i][3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 300000);

					channelers[i] = channeler ? channeler->GetGUID() : 0;
                }
            }

            void JustDied(Unit* /*killer*/)
            {
                Talk(SAY_DIE);
                if (instance)
				{
					// Xinef: load grid with start doors
					me->GetMap()->LoadGrid(0, -111.0f);
					instance->SetData(DATA_KELIDAN, DONE);
					instance->HandleGameObject(instance->GetData64(DATA_DOOR1), true);
					instance->HandleGameObject(instance->GetData64(DATA_DOOR6), true);
				}
            }

			void ApplyImmunities(bool apply)
			{
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_DISTRACT, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SILENCE, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_BANISH, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SHACKLE, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_DAZE, apply);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
			}

            void UpdateAI(uint32 diff)
            {
                if (!UpdateVictim())
                {
					checkTimer += diff;
                    if (checkTimer >= 5000)
                    {
						checkTimer = 0;
                        CheckChannelers();
						if (!me->HasUnitState(UNIT_STATE_CASTING))
                            me->CastSpell(me, SPELL_EVOCATION, false);
                        
                    }
                    return;
                }

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.GetEvent())
				{
					case EVENT_SPELL_VOLLEY:
						me->CastSpell(me, SPELL_SHADOW_BOLT_VOLLEY, false);
						events.RepeatEvent(urand(8000, 13000));
						break;
					case EVENT_SPELL_CORRUPTION:
						me->CastSpell(me, SPELL_CORRUPTION, false);
						events.RepeatEvent(urand(30000, 50000));
						break;
					case EVENT_SPELL_BURNING_NOVA:
						Talk(SAY_NOVA);
						
						ApplyImmunities(false);
						me->AddAura(SPELL_BURNING_NOVA, me);
						ApplyImmunities(true);

						if (IsHeroic())
							DoTeleportAll(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());

						events.DelayEvents(6000, 0);
						events.RepeatEvent(urand(25000, 32000));
						events.ScheduleEvent(EVENT_SPELL_FIRE_NOVA, 5000);
						break;
					case EVENT_SPELL_FIRE_NOVA:
						me->CastSpell(me, SPELL_FIRE_NOVA, true);
						events.PopEvent();
						break;
				}

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new boss_kelidan_the_breakerAI(creature);
        }
};

class npc_shadowmoon_channeler : public CreatureScript
{
    public:

        npc_shadowmoon_channeler() : CreatureScript("npc_shadowmoon_channeler") {}

        struct npc_shadowmoon_channelerAI : public ScriptedAI
        {
            npc_shadowmoon_channelerAI(Creature* creature) : ScriptedAI(creature){}

            EventMap events;

            void Reset()
            {
				events.Reset();
            }

			Creature* GetKelidan()
			{
				if (me->GetInstanceScript())
					return ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetData64(DATA_KELIDAN));
				return NULL;
			}

            void EnterCombat(Unit* who)
            {
                if (Creature* kelidan = GetKelidan())
                    kelidan->AI()->DoAction(ACTION_CHANNELER_ENGAGED);

				me->InterruptNonMeleeSpells(false);
				events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, urand(1500, 3500));
				events.ScheduleEvent(EVENT_SPELL_MARK, urand(5000, 6500));
            }

            void JustDied(Unit* killer)
            {
                if (Creature* kelidan = GetKelidan())
                    kelidan->AI()->DoAction(ACTION_CHANNELER_DIED);
            }

            void UpdateAI(uint32 diff)
            {
                if (!UpdateVictim())
                    return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.GetEvent())
				{
					case EVENT_SPELL_SHADOW_BOLT:
						me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_SHADOW_BOLT_H : SPELL_SHADOW_BOLT, false);
						events.RepeatEvent(urand(6000, 7500));
						break;
					case EVENT_SPELL_MARK:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
							me->CastSpell(target, SPELL_MARK_OF_SHADOW, false);
						events.RepeatEvent(urand(16000, 17500));
						break;
				}

                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return new npc_shadowmoon_channelerAI(creature);
        }
};

void AddSC_boss_kelidan_the_breaker()
{
    new boss_kelidan_the_breaker();
    new npc_shadowmoon_channeler();
}

